


/**
 * Object that represents a date and can calculate the number of days between this date and another
 *
 */
public class Date {
	
	private static final int JAN = 1;
	private static final int FEB = 2;
	private static final int MAR = 3;
	private static final int APR = 4;
	private static final int MAY = 5;
	private static final int JUN = 6;
	private static final int JUL = 7;
	private static final int AUG = 8;
	private static final int SEP = 9;
	private static final int OCT = 10;
	private static final int NOV = 11;
	private static final int DEC = 12;
	
	Integer day		= -1;
	Integer month	= -1;
	Integer year	= -1;
	
	/**
	 * Construct an object with a date string in the format of DD/MM/YYYY
	 * 
	 * @param s
	 * @throws InvalidDateString
	 */
	public Date(String s) throws InvalidDateString {
		parseDateString(s);
	}
	
	/* 
	 * (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return month + "/" + day + "/" + year ;
	}
	
	/**
	 * Parses a string into this object
	 * 
	 * throws an InvalidDateString if the string isn't in the format of DD/MM/YYYY or if the day, month, and year parameters arn't valid
	 * 
	 * @param s
	 * @throws InvalidDateString
	 */
	private void parseDateString(String s) throws InvalidDateString {
		String[] strings = s.split("/");
		if (strings.length != 3) {
			// Error invalid date string
			throw new InvalidDateString("Invalid Date Format");
		}
		
		month = new Integer(strings[0]);
		day   = new Integer(strings[1]);
		year  = new Integer(strings[2]);
		
		if ( year < 1800) {
			throw new InvalidDateString("Date before the year 1800");
		}
		
		if ( year >= 2100) {
			if (!month.equals(1) || !day.equals(1)) {
				throw new InvalidDateString("Date greater than year 2100");
			}
		}
		
		if ( month < 1 || month > 12) {
			throw new InvalidDateString("Month must be a number between 1 and 12");
		}
		
		if (day <= 0 || day > getDaysInTheMonth(month, year)) {
			throw new InvalidDateString("There is not " + day + " days in month " + month);
		}
	}
	
	/**
	 * Returns the number of days in each month
	 * @param month
	 * @param year
	 * @return
	 */
	private int getDaysInTheMonth(int month, int year) {
		
		int numberOfDays = 0;
		
		switch (month) {
			case JAN :
			case MAR :
			case MAY :
			case JUL :
			case AUG :
			case OCT :
			case DEC :
				numberOfDays = 31;
				break;
			case APR :
			case JUN:
			case SEP:
			case NOV:
				numberOfDays = 30;
				break;
			case FEB :
				if (isLeapYear(year)) {
					numberOfDays = 29;
				}
				else {
					numberOfDays = 28;
				}
				break;
		}
		
		return numberOfDays;
	}
	
	// http://en.wikipedia.org/wiki/Leap_year
	/**
	 * check to see if a year is leap year or not
	 * 
	 * got psedocode obtained from http://en.wikipedia.org/wiki/Leap_year
	 * @param year
	 * @return
	 */
	public boolean isLeapYear(int year) {
		boolean leapYear = false;
		if (year % 400 == 0) {
			leapYear = true;
		} else if ( year % 100 == 0) {
			leapYear = false;
		} else if (year % 4 == 0) {
			leapYear = true;
		}
		
		return leapYear;
	}
	
	/**
	 * Calculates the number of days between two dates. 
	 * 
	 * Throws Calculation error if the two dates are not in order
	 * 
	 * @param date2
	 * @return
	 * @throws CalculationError
	 */
	public int calculateNumberOfDaysBetween(Date date2) throws CalculationError {
		int numberOfDays = 1;
		
		if (year > date2.year) {
			throw new CalculationError("date1 comes after date2: year " + year + " > " + date2.year);
		}
		
		if ( year.equals(date2.year) && month > date2.month ) {
			throw new CalculationError("date1 comes after date2: month " + month + " > " + date2.month);
		}
		
		if ( month.equals(date2.month) && day > date2.day ) {
			throw new CalculationError("date1 comes after date2: day " + day + " > " + date2.day);
		}
		
		// At this point we know that this date comes before date2
		
		// Are the two dates in the same year?
		if ( year.equals(date2.year)) {
			if (month.equals(date2.month)) {
				numberOfDays = date2.day - day + 1;
			} else {
				numberOfDays += getDaysInTheMonth(month, year) - day;
				
				// Calculate the partial year
				for (int m = month + 1; m < date2.month; ++m) {
					numberOfDays += getDaysInTheMonth(m, date2.year);
				}
				
				numberOfDays += date2.day;
			}
		} else {
			// Calculate the remaining days in the month
			numberOfDays += getDaysInTheMonth(month, year) - day;
			
			// Calculate the whole years between the 2 dates
			for (int y = year + 1; y < date2.year; ++y) {
				if (isLeapYear(y)) {
					numberOfDays += 366;
				}
				else {
					numberOfDays += 365;
				}
			}
			
			// Calculate the remaining days in the year
			for (int m = month + 1; m <= 12; ++m) {
				numberOfDays += getDaysInTheMonth(m, year);
			}
			
			// Calculate the partial year
			for (int m = 1; m < date2.month; ++m) {
				numberOfDays += getDaysInTheMonth(m, date2.year);
			}
			
			numberOfDays += date2.day;
			
		}
		return numberOfDays - 1;		
	}
	
	
	

}
